package abio

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

var (
	ErrClosed = errors.New("文件已被关闭")
)

//AbFile 可以实现自动备份的文件对象
type AbFile struct {
	absPathName string
	file        *os.File
	reader      *bufio.Reader
	buffer      sync.Pool
	isClosed    bool
	closeChan   chan struct{}
	mutex       sync.Mutex
	option      Options
}

func (ab *AbFile) File() *os.File {
	return ab.file
}

func (ab *AbFile) watch() {
	tk := time.NewTicker(time.Hour * 24)
	defer func() {
		tk.Stop()
		close(ab.closeChan)
	}()
	for {
		select {
		case <-tk.C:
			delDirForDays(ab)
		case <-ab.closeChan:
			_ = ab.Close()
			return
		}
	}
}

func (ab *AbFile) Read(p []byte) (n int, err error) {
	ab.mutex.Lock()
	defer ab.mutex.Unlock()
	if ab.isClosed {
		err = ErrClosed
		return
	}
	return ab.reader.Read(p)
}

func (ab *AbFile) Write(p []byte) (n int, err error) {
	ab.mutex.Lock()
	defer ab.mutex.Unlock()
	if ab.isClosed {
		err = ErrClosed
		return
	}
	if err = backUpPackage(ab); err != nil {
		return
	}
	//p = append([]byte("[AbIO]☞ "), p...)
	//t := fmt.Sprintf("[%s]\n", time.Now().Format("2006-01-02 15:04:05:000000"))
	//p = append([]byte(t), p...)
	buf := ab.buffer.Get().(*bytes.Buffer)
	buf.Truncate(0)
	defer func() {
		ab.buffer.Put(buf)
	}()
	buf.Write(p)
	return ab.file.Write(buf.Bytes())
}

func (ab *AbFile) Close() error {
	ab.isClosed = true
	return ab.file.Close()
}

var abIOMps sync.Map

//NewAbFile 构造函数
func NewAbFile(name string, op *Options) (abFile *AbFile) {
	defer func() {
		if p := recover(); p != nil {
			_ = fmt.Errorf("%v", p)
			abFile = nil
		}
	}()
	absName := AbsPathOfFile(name)
	abInterface, ok := abIOMps.Load(absName)
	if ok {
		return abInterface.(*AbFile)
	}
	abFile = &AbFile{
		absPathName: absName,
		closeChan:   make(chan struct{}),
		buffer: sync.Pool{
			New: func() interface{} {
				return bytes.NewBuffer(make([]byte, 255))
			},
		},
	}
	if op == nil {
		abFile.option = defaultOption
	} else {
		abFile.option = *op
	}
	_ = os.MkdirAll(filepath.Dir(absName), 0755)
	fl, err := os.OpenFile(absName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
	if err != nil {
		panic(err)
	}
	abFile.file = fl
	abFile.reader = bufio.NewReader(fl)
	go abFile.watch()

	abIOMps.Store(absName, abFile)
	return
}

func SelfDir() string {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	i := strings.LastIndex(path, "/")
	if i < 0 {
		i = strings.LastIndex(path, "\\")
	}
	if i < 0 {
		return ""
	}
	return string(path[0 : i+1])
}
